' Camera demo
'
' Demonstrates:
'   Storing a camera position and direction in a matrix
'   Moving the camera around (using a basic aeroplane like mechanism)

' Camera variables
dim camera#(3)(3)               ' Camera position and direction 
dim speed#                      ' Camera speed

' Working variables
dim x, y, temp#(3), xaxis#, yaxis#

' Setup camera
camera# = MatrixTranslate (0, 5, 0)
speed# = 0.02 
               
TextMode (TEXT_OVERLAID)
color (200, 200, 255)
print "Use arrow keys to fly camera"

' Main loop
while true

    ' Render frame
    glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
    
    ' Render from the camera's viewpoint
    ' Notes:
    ' * The camera stores the position and direction of the 
    '   CAMERA relative to the WORLD.
    ' * We want to render the WORLD relative to the CAMERA, 
    '   therefore we need to invert the matrix.
    ' * The camera is built from rotations and translations
    '   therefore we can use RTInvert to correctly invert 
    '   it.
    glLoadMatrixf (RTInvert (camera#))
    
    ' Draw some quads
    for x = -10 to 10
        for y = -10 to 10
            glPushMatrix ()
            glTranslatef (x * 3, 0, y * 3)
            glBegin (GL_QUADS)
                glColor3f ( 1, 0, 0): glVertex2f ( 1, 3)
                glColor3f ( 0, 1, 0): glVertex2f (-1, 3)
                glColor3f ( 0, 0, 1): glVertex2f (-1, 0)
                glColor3f ( 1, 1, 1): glVertex2f ( 1, 0)
            glEnd ()
            glPopMatrix ()
        next
    next
    DrawText ()
    SwapBuffers ()    
    
    ' Update the camera position
    while SyncTimer (10)
    
        ' Move the camera forward.

        ' We can do this by noticing that camera# is a rotation and translation matrix.
        ' Therefore if we split the matrix into columns, we know that the first three 
        ' compose the basis axes of the rotation component.
        ' To put it in simpler terms:
        '   camera#(0) = The camera's LEFT vector
        '   camera#(1) = The camera's UP vector
        '   camera#(2) = The camera's BACKWARD vector
        ' We also know that the right hand column vector camera#(3) contains the
        ' camera's POSITION.
        
        ' Therefore to move the camera forward, we just need to add the camera's 
        ' FORWARD vector (scaled by the current speed) to the camera's POSITION vector.
        ' (Note: We can get the camera's FORWARD vector by negating it's BACKWARD vector
        '  i.e by calculating -camera# (2))
        camera# (3) = camera# (3) + -camera# (2) * speed#    
        
        ' Turn the camera
        
        ' We do this by multiplying the camera matrix by a rotation matrix.
        ' The rotation matrix is multiplied on the RIGHT HAND SIDE of the camera matrix.
        ' Therefore the rotation happens in CAMERA SPACE (i.e relative to the camera).
        
        ' If we rotate around the Z axis (points out of the screen) the camera will bank
        ' If we rotate around the X axis (points left) the camera will pitch.

        xaxis# = -Mouse_XD () * 30
        yaxis# =  Mouse_YD () * 30
        if ScanKeyDown (VK_LEFT)  then xaxis# =  1: endif
        if ScanKeyDown (VK_RIGHT) then xaxis# = -1: endif
        if ScanKeyDown (VK_UP)    then yaxis# = -1: endif
        if ScanKeyDown (VK_DOWN)  then yaxis# =  1: endif
        camera# = camera# * MatrixRotateZ (xaxis#) * MatrixRotateX (yaxis#) 
        
        ' Turn the camera 
        temp# = camera#(3)
        camera#(3) = vec4 (0, 0, 0, 1)
        camera# = MatrixRotateY (camera# (0)(1)) * camera#
        camera#(3) = temp#
        
        ' Orthonormalize the camera matrix.
        
        ' Everytime we multiply the matrix by a rotation, we introduce rounding error.
        ' This rounding error is insignificant for a reasonable number of transformations.
        ' However, as we are updating the camera 100 times per second the camera matrix
        ' will quickly become the result of 1000s of transformations, and eventually the
        ' rounding error can build up to the point where the matrix collapses or explodes.
        
        ' Orthonormalize performs a sequence of cross-products and vector normalizations
        ' to ensure the rotation component of the matrix is orthonormal, and minimize any
        ' accumulated rounding error.
        camera# = Orthonormalize (camera#)
    wend
wend